package com.haizhi.crm.biz.query;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.haizhi.crm.biz.constant.ModuleSourceType;
import com.haizhi.crm.biz.dao.ConcernedDao;
import com.haizhi.crm.biz.dao.FavouriteDao;
import com.haizhi.crm.biz.dao.UserShareDao;
import com.haizhi.crm.biz.model.ConcernedDo;
import com.haizhi.crm.biz.model.THead;
import com.haizhi.crm.biz.model.UserModuleDetailVo;
import com.haizhi.crm.biz.model.UserModuleReqDto;
import com.haizhi.crm.common.constant.CrmSchema;
import com.haizhi.crm.common.json.JSONUtils;
import com.haizhi.crm.common.util.CommonUtils;
import com.haizhi.crm.common.util.Getter;
import com.haizhi.crm.search.builder.SearchQoBuilder;
import com.haizhi.crm.search.model.SearchVo;
import com.haizhi.crm.search.service.SearchService;
import com.haizhi.crm.sys.auth.dao.DepartmentDao;
import com.haizhi.crm.sys.auth.dao.RelationItemDao;
import com.haizhi.crm.sys.auth.dao.UserDao;
import com.haizhi.crm.sys.configure.constant.ModuleSettingCategory;
import com.haizhi.crm.sys.configure.dao.ModuleSettingDao;
import com.haizhi.crm.sys.configure.model.ModuleSettingDo;
import com.haizhi.crm.sys.configure.model.UserModuleSettingCategoryVo;
import com.haizhi.crm.sys.configure.model.UserModuleSettingItemVo;
import com.haizhi.crm.sys.configure.model.UserModuleSettingVo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by wangshan on 2018/6/20.
 */
@Service
public abstract class AbstractQuery {

    private static final String CHECKED = "true";
    @Autowired
    private RelationItemDao relationItemDao;

    @Autowired
    private DepartmentDao departmentDao;

    @Autowired
    private SearchService searchService;

    @Autowired
    private UserDao userDao;

    @Autowired
    private FavouriteDao favouriteDao;

    @Autowired
    private ModuleSettingDao moduleSettingDao;

    @Autowired
    private ConcernedDao concernedDao;

    @Autowired
    private UserShareDao userShareDao;

    /**
     * parse the settingVo to req
     *
     * @param userModuleSettingVo
     * @return
     */
    public UserModuleReqDto parse(UserModuleSettingVo userModuleSettingVo) {
        String title = "";
        int pageSize = 0;
        List<String> fields = new LinkedList<>();
        List<THead> tHeads = new LinkedList<>();
        List<ModuleSourceType> settings = new LinkedList<>();
        List<UserModuleSettingCategoryVo> categorys = userModuleSettingVo.getCategorys();
        for (UserModuleSettingCategoryVo userModuleSettingCategoryVo : categorys) {
            String code = userModuleSettingCategoryVo.getCategoryCode();
            List<UserModuleSettingItemVo> items = null;
            switch (ModuleSettingCategory.codeOf(code)) {
                case MODULE_NAME:
                    items = userModuleSettingCategoryVo.getItems();
                    if (items != null && items.size() != 0)
                        title = items.get(0).getSettingValue();
                    break;
                case DATA_RANGE:
                    settings = userModuleSettingCategoryVo.getItems()
                            .stream().filter(n -> n != null && CHECKED.equals(n.getSettingValue()))
                            .map(n -> ModuleSourceType.getType(moduleSettingDao.findOne(n.getSettingItem().getSettingId()).getName()))
                            .collect(Collectors.toList());
                    break;
                case FIELD:

                    Set<ModuleSettingDo> moduleSettingDos = userModuleSettingCategoryVo.getItems()
                            .stream()
                            .filter(n -> n != null && CHECKED.equals(n.getSettingValue()))
                            .map(n -> moduleSettingDao.findOne(n.getSettingItem().getSettingId()))
                            .collect(Collectors.toSet());
                    for (ModuleSettingDo moduleSettingDo : moduleSettingDos) {
                        fields.add(moduleSettingDo.getName());
                        THead tHead = new THead();
                        tHead.setName(moduleSettingDo.getName());
                        tHead.setShowName(moduleSettingDo.getShowName());
                        tHeads.add(tHead);
                    }
                    break;
                case PAGE_SZIE:
                    items = userModuleSettingCategoryVo.getItems();
                    if (!CollectionUtils.isEmpty(items)) {
                        String settingValue = items.get(0).getSettingValue();
                        if (StringUtils.isNotBlank(settingValue)) {
                            pageSize = Integer.parseInt(settingValue);
                        } else {
                            pageSize = 5;
                        }
                    }
                    break;
            }
        }
        return UserModuleReqDto.builder().fields(fields)
                .pageSize(pageSize).title(title)
                .settings(settings).tHeads(tHeads).build();
    }

    /**
     * get customer key list by source type and user id
     *
     * @param moduleSourceTypes
     * @param userId
     * @return
     */
    public List<String> getSourceCustomer(List<ModuleSourceType> moduleSourceTypes, String schema, Long userId) {
        List<String> results = new ArrayList<>();
        for (ModuleSourceType moduleSourceType : moduleSourceTypes) {
            switch (moduleSourceType) {
                case MY_CONCERN:
                    results.addAll(getConcernCustomer(userId));
                    break;
                case MY_CUSTOMER:
                    results.addAll(getMyCustomer(userId));
                    break;
                case SUBORDINATE_CONCERN:
                    results.addAll(getSubordinateConcernCustomer(userId));
                    break;
                case SUBORDINATE_CUSTOMER:
                    results.addAll(getSubordinateCustomer(userId));
                    break;
                case FAVOURITE:
                    results.addAll(getFavoriteCustomer(userId, schema));
                    break;
                case USER_SHARE:
                    results.addAll(getShareCustomer(userId, schema));
                    break;
            }
        }
        return results;
    }


    /**
     * get my customer (company)
     *
     * @param userId
     * @return
     */
    public List<String> getMyCustomer(Long userId) {
        return relationItemDao.findByUserId(userId).stream().map(n -> n.getEntityKey()).collect(Collectors.toList());
    }

    /**
     * get user share to me customer (company)
     *
     * @param userId
     * @return
     */
    public List<String> getShareCustomer(Long userId, String schema) {
        return getShareCustomer(Arrays.asList(userId), schema);
    }

    /**
     * get user share to me customer (company)
     *
     * @param userIds
     * @return
     */
    public List<String> getShareCustomer(List<Long> userIds, String schema) {
        return userShareDao.findEntityKeys(userIds, schema);
    }

    /**
     * get user favorite customer (company)
     *
     * @param userId
     * @return
     */
    public List<String> getFavoriteCustomer(Long userId, String schema) {
        return getFavoriteCustomer(Arrays.asList(userId), schema);
    }

    /**
     * get user favorite customer (company)
     *
     * @param userIds
     * @return
     */
    public List<String> getFavoriteCustomer(List<Long> userIds, String schema) {
        return favouriteDao.findEntityKeys(userIds, schema);
    }

    /**
     * get i concerned company
     *
     * @param userId
     * @return
     */
    public List<String> getConcernCustomer(Long userId) {
        return getConcernCustomer(Arrays.asList(userId));
    }


    /**
     * get my subordinate concerned company
     *
     * @param managerId
     * @return
     */
    public List<String> getSubordinateConcernCustomer(Long managerId) {
        List<Long> userIds = getSubordinate(managerId);
        return getConcernCustomer(userIds);
    }

    /**
     * get i concerned company
     *
     * @param userIds
     * @return
     */
    public List<String> getConcernCustomer(List<Long> userIds) {
        List<String> results = new ArrayList<>();
        if (CollectionUtils.isEmpty(userIds))
            return results;
        return concernedDao.findByUserIds(userIds);
    }

    /**
     * get my subordinate customer
     *
     * @param managerId
     * @return
     */
    public List<String> getSubordinateCustomer(Long managerId) {
        List<Long> userIds = getSubordinate(managerId);
        List<String> results = new ArrayList<>();
        if (CollectionUtils.isEmpty(userIds))
            return results;

        for (Long userId : userIds) {
            results.addAll(relationItemDao.findByUserId(userId).stream().map(n -> n.getEntityKey()).collect(Collectors.toList()));
        }
        return results;
    }

    /**
     * get my subordinate
     *
     * @param managerId
     * @return
     */
    public List<Long> getSubordinate(Long managerId) {
        Long departmentId = departmentDao.findByManagerId(managerId);
        if (departmentId == null) {
            return null;
        }
        List<Long> userIds = userDao.findByDeptId(departmentId).stream().map(n -> n.longValue()).collect(Collectors.toList());
        return userIds;
    }

    /**
     * default query method
     *
     * @param userModuleSettingVo
     * @return
     */
    public UserModuleDetailVo query(UserModuleSettingVo userModuleSettingVo, String schema, String fieldName) {
        Long userId = CommonUtils.getUserID();
        UserModuleReqDto userModuleReqDto = parse(userModuleSettingVo);
        List<String> companyKeys = getSourceCustomer(userModuleReqDto.getSettings(), schema, userId);

        SearchQoBuilder searchQoBuilder = new SearchQoBuilder();
        searchQoBuilder.setGraph(CrmSchema.CRM);
        searchQoBuilder.addSchemas(schema);
        searchQoBuilder.setPageSize(userModuleReqDto.getPageSize());
        searchQoBuilder.addTermFilter(fieldName, companyKeys.toArray());
        if (CrmSchema.EVENT_OUTTER.equals(schema) || CrmSchema.EVENT_INNER.equals(schema)) {
            searchQoBuilder.addSort("event_time", "desc" );
        } else {
            searchQoBuilder.addSort("reg_date", "desc" );
        }
        SearchVo searchVo = searchService.search(searchQoBuilder.get());

        String jsonData = JSON.toJSONString(searchVo.getSearchResp().getData());
        List<Map<String, Object>> companys = JSONUtils.jsonToListMap(jsonData);
        List<Object> datas = new LinkedList<>();
        for (Map<String, Object> obj : companys) {
            Map<String, Object> data = new LinkedHashMap<>();
            for (String field : userModuleReqDto.getFields()) {
                if ("customer".equals(field) && CrmSchema.EVENT_OUTTER.equals(schema)) {
                    buildOuterEventCustomer(obj, data, field);
                } else if ("customer".equals(field) && CrmSchema.EVENT_INNER.equals(schema)) {
                    buildInnerEventCustomer(obj, data, field);
                } else {
                    data.put(field, obj.getOrDefault(field, "" ));
                }
            }
            String objectKey = Getter.get("object_key", obj);
            setFavoriteAndConcerned(schema, data, objectKey);
            datas.add(data);
        }
        return UserModuleDetailVo.builder().datas(datas)
                .settings(userModuleReqDto.getTHeads())
                .title(userModuleReqDto.getTitle())
                .userModuleId(userModuleSettingVo.getUserModuleId()).build();
    }

    private void buildInnerEventCustomer(Map<String, Object> obj, Map<String, Object> data, String field) {
        String customerKey = Getter.get("customer_key", obj);
        SearchQoBuilder builder = new SearchQoBuilder();
        builder.setGraph(CrmSchema.CRM);
        builder.addSchemas(CrmSchema.COMPANY);
        builder.addTermFilter("object_key", customerKey);
        Object customer = searchService.search(builder.get()).getSearchResp().getData();
        Map<String, Object> customerMap = JSONUtils.toMap(customer);

        Map<String, Object> companyMap = new LinkedHashMap<>();
        companyMap.put("objectKey", Getter.get("object_key", customerMap));
        companyMap.put("name", Getter.get("name", customerMap));
        List<Map<String, Object>> customerList = new ArrayList<>();
        customerList.add(companyMap);
        data.put(field, customerList);
    }

    private void buildOuterEventCustomer(Map<String, Object> obj, Map<String, Object> data, String field) {
        String companykeys = Getter.get("company_keys", obj);
        JSONArray jsonArray = JSON.parseArray(companykeys);
        List<String> companyObjects = new ArrayList<>();
        for (int i = 0; i < jsonArray.size(); i++) {
            companyObjects.add(jsonArray.get(i).toString());
        }
        SearchQoBuilder builder = new SearchQoBuilder();
        builder.setGraph(CrmSchema.CRM);
        builder.addSchemas(CrmSchema.COMPANY);
        builder.addTermFilter("object_key", companyObjects.toArray());
        Object customer = searchService.search(builder.get()).getSearchResp().getData();
        List<Map<String, Object>> customers = JSONUtils.toListMap(customer);

        List<Map<String, Object>> customerList = new ArrayList<>();
        customers.stream().forEach(c ->
        {
            Map<String, Object> companyMap = new LinkedHashMap<>();
            companyMap.put("objectKey", Getter.get("object_key", c));
            companyMap.put("name", Getter.get("name", c));
            customerList.add(companyMap);
        });
        data.put(field, customerList);
    }

    private void setFavoriteAndConcerned(String schema, Map<String, Object> data, String objectKey) {
        data.put("object_key", objectKey);
        if (CrmSchema.EVENT_INNER.equals(schema) || CrmSchema.EVENT_OUTTER.equals(schema)) {
            int favouriteCount = favouriteDao.findFavouriteCount(CommonUtils.getUserID(), schema, objectKey);
            data.put("favorited", favouriteCount >= 1 ? true : false);
        } else {
            List<ConcernedDo> concernedDos = concernedDao.findByCompanyKeyAndUserId(objectKey, CommonUtils.getUserID());
            data.put("concerned", CollectionUtils.isEmpty(concernedDos) ? false : true);
        }
    }
}
